// Copyright (c) 2015 David Muse
// See the COPYING file for more information.

#include <wikitext/private/dll.h>
#include <rudiments/compiler.h>

struct nameswap {
	const char	*oldname;
	const char	*newname;
};

class WIKITEXT_DLLSPEC plaintext_processor : public compilerprocessor {
	public:
			plaintext_processor();
		bool	process(xmldomnode *tree,
				xmldomnode *metadata);
	private:
		xmldomnode	*handleDefinition(xmldomnode *node);
		xmldomnode	*wrapTable(xmldomnode *node);

		void		handleExternalLink(xmldomnode *node);
};

static const char *unwrap[]={
	"doc",
	"esc",
	"b",
	"i",
	"tt",
	"u",
	"strike",
	"sup",
	"sub",
	"pre",
	"proto",
	"a_link",
	"i_link",
	"e_link",
	NULL
};

static const char *remove[]={
	"h_id",
	"a_name",
	"url",
	"img",
	"cmt",
	"bq",
	NULL
};

plaintext_processor::plaintext_processor() {
}

bool plaintext_processor::process(xmldomnode *tree, xmldomnode *metadata) {

	xmldomnode *child=tree->getFirstTagChild();
	while (!child->isNullNode()) {

		// get next in case we rebuild the tree a bit
		xmldomnode	*next=child->getNextTagSibling();

		// get the node name
		const char	*nodens=child->getNamespace();
		const char	*nodename=child->getName();

		// remove some nodes outright
		bool	removed=false;
		for (const char * const *u=remove; *u; u++) {
			if (!charstring::compare(nodename,*u)) {
				uint64_t	pos=child->getPosition();
				tree->deleteChild(child);
				child=tree->getChild(pos);
				removed=true;
				break;
			}
		}
		if (removed) {
			continue;
		}

		// handle external links before unwrapping, then fall
		// through to unwrap them
		if (!charstring::compare(nodename,"e_link")) {
			handleExternalLink(child);
		}

		// unwrap
		bool	unwrapped=false;
		for (const char * const *u=unwrap; *u; u++) {
			if (!charstring::compare(nodename,*u)) {
				uint64_t	pos=child->getPosition();
				tree->unwrapChild(child);
				child=tree->getChild(pos);
				unwrapped=true;
				break;
			}
		}
		if (unwrapped) {
			continue;
		}

		// handle definitions
		if (!charstring::compare(nodename,"def")) {
			child=handleDefinition(child);
			continue;
		}

		// handle tables
		if (!charstring::compare(nodens,"wt") &&
			(!charstring::compare(nodename,"th") ||
			!charstring::compare(nodename,"td"))) {
			child=wrapTable(child);
			continue;
		}

		// descend
		if (!process(child,metadata)) {
			return false;
		}

		// move on
		child=next;
	}

	return true;
}

xmldomnode *plaintext_processor::handleDefinition(xmldomnode *child) {
	child->deleteChildren("opt_ws");
	xmldomnode	*term=child->getFirstTagChild();
	child->getParent()->unwrapChild(child);
	return term;
}

xmldomnode *plaintext_processor::wrapTable(xmldomnode *child) {

	// wrap runs, renaming as we go...

	for (;;) {

		// find the run of headers and cells...
		xmldomnode	*start=child;
		xmldomnode	*end=child;
		for (;;) {
			if (!charstring::compare(end->getNamespace(),"wt") &&
				(!charstring::compare(end->getName(),"th") ||
				!charstring::compare(end->getName(),"td"))) {
				end->setNamespace("xhtml");
			}
			xmldomnode	*next=end->getNextTagSibling();
			if (!charstring::compare(next->getNamespace(),"wt") &&
				(!charstring::compare(next->getName(),"th") ||
				!charstring::compare(next->getName(),"td"))) {
				end=next;
			} else {
				break;
			}
		}

		child=end->getNextTagSibling();

		// If the next child after the run is a tbr then
		// remove it.  If we find something other than a tbr
		// then we're at the end of the table.
		bool	endoftable=false;
		if (!charstring::compare(child->getName(),"tbr")) {
			xmldomnode	*next=child->getNextTagSibling();
			child->getParent()->deleteChild(child);
			child=next;
		} else {
			endoftable=true;
		}

		// wrap the run of headers/cells with a tr
		end=start->getParent()->wrapChildren(start,end,"pt","tr");

		// if we're at the end of the table then back up,
		// wrap the table, and return the table node
		if (endoftable) {
			start=end;
			for (;;) {
				if (!charstring::compare(
					start->getPreviousTagSibling()->
					getName(),"tr")) {
					start=start->getPreviousTagSibling();
				} else {
					break;
				}
			}
			return start->getParent()->
				wrapChildren(start,end,"pt","table");
		}
	}
}

void plaintext_processor::handleExternalLink(xmldomnode *node) {

	xmldomnode	*url=node->getFirstTagChild("url");

	// remove leading whitespace from text
	xmldomnode	*urlsibling=url->getNextTagSibling();
	if (!charstring::compare(urlsibling->getName(),"ws")) {
		node->deleteChild(urlsibling);
	}
}

extern "C" {
	WIKITEXT_DLLSPEC compilerprocessor *new_plaintext_processor() {
		return new plaintext_processor();
	}
}
